import { addError } from 'markdownlint-rule-helpers'
import { getRange } from '../helpers/utils'
import frontmatter from '@/frame/lib/read-frontmatter'

import type { RuleParams, RuleErrorCallback } from '@/content-linter/types'

// Regex to detect table rows (must start with |, contain at least one more |, and end with optional whitespace)
const TABLE_ROW_REGEX = /^\s*\|.*\|\s*$/
// Regex to detect table separator rows (contains only |, :, -, and whitespace)
const TABLE_SEPARATOR_REGEX = /^\s*\|[\s\-:|\s]*\|\s*$/
// Regex to detect Liquid-only cells (whitespace, liquid tag, whitespace)
const LIQUID_ONLY_CELL_REGEX = /^\s*{%\s*(ifversion|else|endif|elsif|for|endfor).*%}\s*$/
// Regex to use for splitting on non-escaped pipes only
const NON_ESCAPED_PIPE_REGEX = /(?<!\\)\|/
/**
 * Counts the number of columns in a table row by splitting on | and handling edge cases
 */
function countColumns(row: string): number {
  // Remove leading and trailing whitespace
  const trimmed = row.trim()

  // Handle empty rows
  if (!trimmed || !trimmed.includes('|')) {
    return 0
  }

  // Split by '|' (but ignore escaped '\|' as these are not true separators)
  // Filter out empty cells at start/end (from leading/trailing |)
  const cells = trimmed.split(NON_ESCAPED_PIPE_REGEX)

  // Remove first and last elements if they're empty (from leading/trailing |)
  if (cells.length > 0 && cells[0].trim() === '') {
    cells.shift()
  }
  if (cells.length > 0 && cells[cells.length - 1].trim() === '') {
    cells.pop()
  }

  return cells.length
}

/**
 * Checks if a table row contains only Liquid conditionals
 */
function isLiquidOnlyRow(row: string): boolean {
  const trimmed = row.trim()
  if (!trimmed.includes('|')) return false

  const cells = trimmed.split(NON_ESCAPED_PIPE_REGEX)
  // Remove empty cells from leading/trailing |
  const filteredCells = cells.filter((cell, index) => {
    if (index === 0 && cell.trim() === '') return false
    if (index === cells.length - 1 && cell.trim() === '') return false
    return true
  })

  // Check if all cells contain only Liquid tags
  return (
    filteredCells.length > 0 && filteredCells.every((cell) => LIQUID_ONLY_CELL_REGEX.test(cell))
  )
}

export const tableColumnIntegrity = {
  names: ['GHD047', 'table-column-integrity'],
  description: 'Tables must have consistent column counts across all rows',
  tags: ['tables', 'accessibility', 'formatting'],
  severity: 'error',
  function: (params: RuleParams, onError: RuleErrorCallback) => {
    // Skip autogenerated files
    const frontmatterString = params.frontMatterLines.join('\n')
    const fm = frontmatter(frontmatterString).data
    if (fm && fm.autogenerated) return

    const lines = params.lines
    let inTable = false
    let inCodeFence = false
    let expectedColumnCount: number | null = null

    for (let i = 0; i < lines.length; i++) {
      const line = lines[i]

      // Toggle code fence state
      if (line.trim().startsWith('```')) {
        inCodeFence = !inCodeFence
        continue
      }

      if (inCodeFence) {
        continue
      }

      const isTableRow = TABLE_ROW_REGEX.test(line)
      const isSeparatorRow = TABLE_SEPARATOR_REGEX.test(line)

      // Check if we're starting a new table
      if (!inTable && isTableRow) {
        // Look ahead to see if next line is a separator (confirming this is a table)
        const nextLine = lines[i + 1]
        if (nextLine && TABLE_SEPARATOR_REGEX.test(nextLine)) {
          inTable = true
          expectedColumnCount = countColumns(line)
          continue
        }
      }

      // Check if we're ending a table
      if (inTable && !isTableRow) {
        inTable = false
        expectedColumnCount = null
        continue
      }

      // If we're in a table, validate column count
      if (inTable && isTableRow && !isSeparatorRow) {
        // Skip Liquid-only rows as they're allowed to have different column counts
        if (isLiquidOnlyRow(line)) {
          continue
        }

        const actualColumnCount = countColumns(line)

        if (actualColumnCount !== expectedColumnCount) {
          const range = getRange(line, line.trim())
          let errorMessage

          if (actualColumnCount > expectedColumnCount!) {
            errorMessage = `Table row has ${actualColumnCount} columns but header has ${expectedColumnCount}. Add ${actualColumnCount - expectedColumnCount!} more column(s) to the header row to match this row.`
          } else {
            errorMessage = `Table row has ${actualColumnCount} columns but header has ${expectedColumnCount}. Add ${expectedColumnCount! - actualColumnCount} missing column(s) to this row.`
          }

          addError(
            onError,
            i + 1,
            errorMessage,
            line,
            range,
            null, // No auto-fix available due to complexity
          )
        }
      }
    }
  },
}
